<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .error {
        outline: none;
        border-color: red;
      }
      span.error {
        color: red;
      }
      .success {
        outline: none;
        border-color: green;
      }
    </style>
  </head>
  <body>
    <div id="app"></div>
    <template id="t1">
      <div>
        <form id="form">
          <p>
            姓名
            <!-- 数据的双向绑定 -->
            <input
              type="text"
              data-valid="username"
              :value="form.username"
              @input="form.username = $event.target.value"
            />
          </p>
          <p>
            邮箱
            <input
              type="text"
              data-valid="email"
              :value="form.email"
              @input="form.email=$event.target.value"
            />
          </p>
          <p>
            密码
            <input
              type="password"
              data-valid="password"
              :value="form.password"
              @input="form.password=$event.target.value"
            />
          </p>
          <p>
            确认
            <input
              type="password"
              :value="form.cpassword"
              data-valid="cpassword"
              @input="form.cpassword=$event.target.value"
            />
          </p>
          <p>
            性别
            <input
              type="radio"
              name="gender"
              @input="form.gender = $event.target.value"
              :checked="form.gender==='1'"
              value="1"
            />
            男
            <input
              type="radio"
              @input="form.gender = $event.target.value"
              :checked="form.gender==='0'"
              name="gender"
              value="0"
            />
            女
          </p>

          <p>
            爱好
            <input
              @input="bindCheckbox"
              type="checkbox"
              value="html"
              name="hobby"
              :checked="form.hobbys.includes('html')"
            />HTML
            <input
              @input="bindCheckbox"
              type="checkbox"
              value="css"
              name="hobby"
              :checked="form.hobbys.includes('css')"
            />CSS
            <input
              @input="bindCheckbox"
              type="checkbox"
              checked
              value="js"
              name="hobby"
              :checked="form.hobbys.includes('js')"
            />JS
          </p>

          <p>
            城市

            <select @input="form.city=$event.target.value">
              <option :selected="form.city==='nj'" value="nj">南京</option>
              <option :selected="form.city==='sh'" value="sh">上海</option>
              <option :selected="form.city==='wx'" value="wx">无锡</option>
              <option :selected="form.city==='sz'" value="sz">苏州</option>
            </select>
          </p>
          <p>
            <button type="button" @click="save">提交</button>
          </p>
          {{form}}
        </form>
      </div>
    </template>
    <script src="./vue.js"></script>
    <script>
      // 数据驱动视图
      const vm = new Vue({
        el: "#app",
        template: "#t1",
        data: {
          form: {
            // 数据->视图
            username: "jack",
            email: "jack@qq.com",
            password: "123",
            cpassword: "123",
            gender: "0",
            hobbys: ["css", "js", "html"],
            city: "sz",
          },
          rule: {
            cpassword: {
              message: "两次密码输入不一致",
              validator(value) {
                return this.form.password === value;
              },
            },
            password: {
              message: "密码必须3-18位",
              validator(value) {
                return /^.{3,18}$/.test(value);
              },
            },
            username: {
              message: "用户名必须2-18个字符",
              validator(value) {
                return /^\w{2,18}$/.test(value);
              },
            },
            email: {
              message: "邮箱格式错误",
              validator: (value) => {
                return /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
                  value
                );
              },
            },
          },
        },
        created() {
          //事件代理
          document.addEventListener("input", (e) => {
            const name = e.target.dataset.valid;
            if (name === undefined) return;
            this.valid(name, e.target);
          });
        },
        methods: {
          bindCheckbox(e) {
            if (e.target.checked) {
              this.form.hobbys.push(e.target.value);
            } else {
              const i = this.form.hobbys.findIndex(
                (el) => el === e.target.value
              );
              this.form.hobbys.splice(i, 1);
            }
          },
          save() {
            if (this.validAll()) {
              console.bindCheckbox("success");
            }
          },
          validAll() {
            // 获取需要验证的表单元素
            const els = [...document.querySelectorAll("[data-valid]")];
            let res = true;
            // 一次性验证所有的输入
            els.forEach((el) => {
              res = res && this.valid(el.dataset.valid, el);
            });
            return res;
          },
          valid(name, target) {
            const ruleName = this.rule[name];
            const formName = this.form[name];
            if (ruleName === undefined || formName === undefined) return;
            // 给validator传递输入参数
            const r = ruleName.validator.call(this, formName);

            // 判断相同span节点是否存在，保证同id span只创建一次
            let span = document.getElementById(`err_${name}`);
            if (span === null) {
              // 创建错误提示信息
              span = document.createElement("span");
              span.id = `err_${name}`;
              target.after(span);
            }
            if (r === false) {
              // 验证错误
              target.classList.add("error");
              target.classList.remove("success");

              span.classList.add("error");

              span.textContent = ruleName.message;
            } else {
              // 验证成功
              target.classList.add("success");
              target.classList.remove("error");
              span.classList.remove("error");
              span.textContent = "";
            }
            return r;
          },
        },
      });
    </script>
  </body>
</html>
